﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace YasharEl.Infrastructure.EntityModel.Specifications.QueryableSpecification
{
    /// <summary>
    /// Base class for <see cref="IQueryable"/> based specifications.
    /// </summary>
    public abstract class QueryableSpecification<TEntity, TIdentity> : ISpecification<TEntity, TIdentity>
        where TEntity : class, IGenericEntity<TIdentity>
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="unitOfWorkConvertor">Interface provides functionality to convert
        /// unit of work to <see cref="IQueryable"/>.</param>
        public QueryableSpecification(IUnitOfWorkConvertor unitOfWorkConvertor)
        {
            Contract.Requires<ArgumentNullException>(unitOfWorkConvertor != null, "unitOfWorkConvertor could not be null.");
            this.UnitOfWorkConvertor = unitOfWorkConvertor;
        }

        /// <summary>
        /// Gets or sets the queryable instance.
        /// </summary>
        protected IQueryable<TEntity> Queryable { get; set; }

        /// <summary>
        /// Gets unit of work convertor.
        /// </summary>
        protected IUnitOfWorkConvertor UnitOfWorkConvertor { get; private set; }

        /// <summary>
        /// Initializes specification from given unit of work.
        /// Implementation varies on <see cref="IUnitOfWork"/> implementation.
        /// </summary>
        public virtual void Initialize(IUnitOfWork unitOfWork)
        {
            this.Queryable = this.UnitOfWorkConvertor.ToQueryable<TEntity, TIdentity>(unitOfWork);
        }

        /// <summary>
        /// Returns queryable specification result.
        /// </summary>
        public ISpecificationResult<TEntity, TIdentity> ToResult()
        {
            return new QueryableSpecificationResult<TEntity, TIdentity>(Queryable);
        }

        public ISpecification<TEntity, TIdentity> Where(Expression<Func<TEntity, bool>> expression)
        {
            this.Queryable.Where(expression);
            return this;
        }
    }
}
